package MethodGraph;

import java.util.*;

import antlr.collections.AST;

import JavaGrammar.*;
import FlowGraph.Nodes.*;
import FlowGraph.*;

//////////////////////////////////////////////////////////////////////////
//// MethodGraphNode
/**

@author Ivan Jeukens
@version v 0.1 04/06/2002
*/
public class MethodGraphNode {
    
    /* se o fgNode for null, significa que e um no auxiliar. Esses nos
    sao utilizados para source e sink do grafo, e no comeco de um
    loop que tenha metodos de portos */
    public MethodGraphNode(MethodCallNode fgNode) {
        _fgNode = fgNode;

        _inputEdges = new LinkedList();
        _outputEdges = new LinkedList();

        _type = -1;
        _loopBodyNodes = new HashMap();
        _firstLoopNodes = new LinkedList();
        _continueLoopNodes = new LinkedList();        
    }

    // Quatro tipos de nos auxliares: source, sink, cabeca de loop, e source
    // associado a cada porto
    public static final int SOURCE = 0;
    public static final int SINK = 1;
    public static final int SEND = 2;
    public static final int SENDARRAY = 3;
    public static final int GET = 4;
    public static final int GETARRAY = 5;
    public static final int HASTOKEN = 6;
    public static final int HASROOM = 7;
    public static final int BROADCAST = 8;
    public static final int BROADCASTARRAY = 9;
    public static final int ISKNOWN = 10;

    public static final int LOOPHEAD = 12;
    public static final int GRAPH_LINK = 13;
    public static final int SENDCLEAR = 14;
    public static final int BROADCASTCLEAR = 15;

    // Quando nao tem uma constante no campo channelindex.
    public static final int ALLCHANNELS = -2;
    public static final int UNDEFCHANNEL = -1;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    // arestas de input
    public void addInputEdge(MethodGraphEdge edge) {
        _inputEdges.add(edge);
    }

    public MethodGraphNode getInputNode(int index) {
        MethodGraphEdge edge = (MethodGraphEdge) _inputEdges.get(index);
        return edge.getFrom();
    }

    public int inputSize() {
        return _inputEdges.size();
    }

    public Iterator inputEdgesIterator() {
        return _inputEdges.iterator();
    }
    
    public MethodGraphEdge getInputEdge(int index) {
        return (MethodGraphEdge) _inputEdges.get(index);
    }

    // arestas de output
    public void addOutputEdge(MethodGraphEdge edge) {
        _outputEdges.add(edge);
    }

    public MethodGraphNode getOutputNode(int index) {
        MethodGraphEdge edge = (MethodGraphEdge) _outputEdges.get(index);
        return edge.getTo();
    }

    public MethodGraphEdge getOutputEdge(int index) {
        return (MethodGraphEdge) _outputEdges.get(index);
    }

    public int outputSize() {
        return _outputEdges.size();
    }

    public Iterator outputEdgesIterator() {
        return _outputEdges.iterator();
    }

    public String getPort() {
        return _port;
    }

    public int getType() {
        return _type;
    }
    
    public void setType(int type) {
        _type = type;
    }

    public String toString() {
        StringBuffer ret = new StringBuffer();
        
        ret.append("Port " + _port + "(" + _channel + ") type ");
        ret.append(_typeToString(_type) + " ntokens = " + _ntokens);
        
        ret.append("\n");
        return ret.toString();
    }

    public String statementString() {
        return _fgNode.statementToString();
    }
    
    public MethodCallNode getMethodCallNode() {
        return _fgNode;
    }

    public MethodGraphEdge getOutputEdge(MethodGraphNode to) {
        for(int i = 0;i < _outputEdges.size();i++) {
            MethodGraphEdge edge = (MethodGraphEdge) _outputEdges.get(i);
            if(edge.getTo() == to) return edge;
        }
        return null;
    }

    public void removeInputEdge(MethodGraphEdge edge) {
        _inputEdges.remove(edge);
    }

    public void removeOutputEdge(MethodGraphEdge edge) {
        _outputEdges.remove(edge);
    }

    public MethodGraph getMethodGraph() {
        if(_type != GRAPH_LINK) return null;
        FlowGraph fg = _fgNode.getFlowGraph();
        if(!fg.isBuild()) return null;

        return fg.getMethodGraph();
    }

    public void setLoopHead(FlowGraphNode node) {
        _loopHead = node;
    }
    
    public FlowGraphNode getLoopHead() {
        return _loopHead;
    }
    
    public int getChannel() {
        return _channel;
    }
    
    public int getNTokens() {
        return _ntokens;
    }

    public void checkContainsIOMethods() {
        if(_type == GRAPH_LINK) {
            FlowGraph fg = _fgNode.getFlowGraph();
            HashMap map = new HashMap();
            if(fg != null && fg.isBuild() && fg.containsIOMethod(map)) {
                _containsIOMethods = true;
            }
        }
        else
        if(_type == LOOPHEAD) {
            if(_containsIO(_loopHead)) _containsIOMethods = true;
        }
    }

    public void extractInfo() {
        _extractInfo();        
    }
    
    public void addLoopBodyNode(MethodGraphNode mgnode) {
        _loopBodyNodes.put(mgnode, null);
    }
    
    public Iterator loopBodyNodesIterator() {
        return _loopBodyNodes.keySet().iterator();
    }

    public void insideLoopBody(MethodGraphNode mgnode) {
        _insideLoop = mgnode;
    }
    
    public MethodGraphNode getInsideLoopBody() {
        return _insideLoop;
    }

    public void setPortsSampleRates(HashMap portsSampleRates) {
        _portsSampleRates = portsSampleRates;
    }
    
    public HashMap getPortsSampleRates() {
        return _portsSampleRates;
    }
    
    public void setSRPortsSampleRates(HashMap portsSampleRates) {
        _SRportsSampleRates = portsSampleRates;
    }
    
    public HashMap getSRPortsSampleRates() {
        return _SRportsSampleRates;
    }
    
    public void addFirstLoopNode(MethodGraphNode node) {
        _firstLoopNodes.add(node);
    }

    public Iterator firstLoopNodesIterator() {
        return _firstLoopNodes.iterator();
    }

    public boolean isFirstLoopNode(MethodGraphNode node) {
        return _firstLoopNodes.contains(node);
    }
    
    public void addContinueLoopNode(MethodGraphNode node) {
        _continueLoopNodes.add(node);
    }

    public Iterator continueLoopNodesIterator() {
        return _continueLoopNodes.iterator();
    }

    public void setContainsIOMethods() {
        _containsIOMethods = true;
    }

    public boolean containsIOMethods() {
        return _containsIOMethods;
    }
    
    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    //// 

    private boolean _containsIO(FlowGraphNode node) {
        LinkedList process = new LinkedList();

        Iterator ite = node.outputEdgesIterator();
        while(ite.hasNext()) {
            FlowGraphEdge edge = (FlowGraphEdge) ite.next();
            if(edge.isBack() || edge.isDeleted()) continue;

            FlowGraphNode n = edge.getTo();
            if(n instanceof LoopEndNode) continue;
            process.add(node);
        }

        HashMap processed = new HashMap();
        while(process.size() > 0) {
            FlowGraphNode n = (FlowGraphNode) process.remove(0);
            processed.put(n, null);
            
            if(n.getType() == FlowGraphNode.METHOD_CALL) {
                if(MethodGraph.isIOFGNode(n)) return true;

                FlowGraph fg = ((MethodCallNode) n).getFlowGraph();
                HashMap map = new HashMap();
                if(fg != null && fg.isBuild() && fg.containsIOMethod(map)) {
                    return true;
                }
            }

            ite = n.outputEdgesIterator();
            while(ite.hasNext()) {
                FlowGraphEdge edge = (FlowGraphEdge) ite.next();
                if(edge.isBack() || edge.isDeleted()) continue;

                if(edge.isException()) continue;

                FlowGraphNode outn = edge.getTo();

                if(outn instanceof LoopEndNode) continue;
                if(processed.containsKey(outn)) continue;
                process.add(outn);
            }
        }

        return false;
    }
    
    private String _typeToString(int type) {
        switch(type) {
            case SOURCE: return "source";
            case SINK: return "sink";
            case SEND: return "send";
            case SENDARRAY: return "sendArray";
            case GET: return "get";
            case GETARRAY: return "getArray";
            case HASTOKEN: return "hasToken";
            case HASROOM: return "hasRoom";
            case BROADCAST: return "broadcast";
            case BROADCASTARRAY: return "broadcastArray";
            case ISKNOWN: return "isKnown";
            
            case LOOPHEAD: return "loopHead";
            case GRAPH_LINK: return "graphLink";
            case SENDCLEAR: return "sendClear";
            case BROADCASTCLEAR: return "broadcastClear";
        }
        return "unknown type!";
    }

    // considero o timed igual a false
    private void _extractInfo() {

        if(_type == LOOPHEAD || _type == GRAPH_LINK || _type == SOURCE ||
            _type == SINK) return;

        // nao esta certo, pois pode ter this.port
        _port = _fgNode.getCallerName();
        AST parast = _fgNode.getParametersList();

        String methodName = _fgNode.getMethodName();
        if(methodName.equals("send")) {
           if(_fgNode.parametersSize() == 2) {
               _type = SEND;
               _ntokens = 1;
           }
           else
           if(_fgNode.parametersSize() == 3) {
               _type = SENDARRAY;
               _ntokens = _getIntPar(parast, 2);
           }
           _channel = _getChannelPar(parast, 0);
        }
        else
        if(methodName.equals("broadcast")) {
            if(_fgNode.parametersSize() == 1) {
                _type = BROADCAST;
                _ntokens = 1;
            }
            else
            if(_fgNode.parametersSize() == 2) {
                _type = BROADCASTARRAY;
                _ntokens = _getIntPar(parast, 1);
            }
            _channel = 0;
        }
        else
        if(methodName.equals("get")) {
            if(_fgNode.parametersSize() == 1) {
                _type = GET;
                _ntokens = 1;
            }
            else
            if(_fgNode.parametersSize() == 2) {
                _type = GETARRAY;
                _ntokens = _getIntPar(parast, 1);
            }
            else {
                System.out.println("MethodGraphNode error: method get with " +
                    " more than 2 parameters");
            }
            _channel = _getChannelPar(parast, 0);
        }
        else
        if(methodName.equals("hasToken")) {
            _type = HASTOKEN;
            if(_fgNode.parametersSize() == 1) {
                _ntokens = 1;
            }
            else
            if(_fgNode.parametersSize() == 2) {
                _ntokens = _getIntPar(parast, 1);
            }
            else {
                System.out.println("MethodGraphNode error: method hastoken with " +
                    " more than 2 parameters");
            }
            _channel = _getChannelPar(parast, 0);
        }
        else
        if(methodName.equals("hasRoom")) {
            _type = HASROOM;
            _ntokens = 1;
            _channel = _getChannelPar(parast, 0);
        }
        else
        if(methodName.equals("isKnown")) {
            _type = ISKNOWN;
            if(_fgNode.parametersSize() == 0) {
                _channel = 0;
            }
            else
            if(_fgNode.parametersSize() == 1) {
                _channel = _getChannelPar(parast, 0);
            }
            _ntokens = 1;
        }
        else        
        if(methodName.equals("sendClear")) {
            _type = SENDCLEAR;
            _ntokens = 1;
            _channel = _getChannelPar(parast, 0);
        }
        else
        if(methodName.equals("broadcastClear")) {
            _type = BROADCASTCLEAR;
            _ntokens = 1;
            _channel = 0;
        }

        if(_insideLoop != null) {
            FlowGraphNode f = _insideLoop.getLoopHead();
            if(f.getType() == FlowGraphNode.FOR_BEGIN) {
                ForBeginNode fbeg = (ForBeginNode) f;
                _ntokens = _ntokens * fbeg.getIterations();
            }
            else
            if(f.getType() == FlowGraphNode.DO_BEGIN) {
                DoBeginNode beg = (DoBeginNode) f;
                _ntokens = _ntokens * beg.getIterations();
            }
            else
            if(f.getType() == FlowGraphNode.WHILE_BEGIN) {
                WhileBeginNode beg = (WhileBeginNode) f;
                _ntokens = _ntokens * beg.getIterations();
            }
        }
    }

    private int _getIntPar(AST ast, int pos) {
        AST v = ast.getFirstChild();
        for(int i = 0;i < pos;i++) v = v.getNextSibling();
        v = v.getFirstChild();

        if(v.getType() != JavaTokenTypes.NUM_INT) return -1;
        return (new Integer(v.getText())).intValue();
    }

    private int _getChannelPar(AST ast, int pos) {
        AST v = ast.getFirstChild();
        for(int i = 0;i < pos;i++) v = v.getNextSibling();
        v = v.getFirstChild();

        if(v.getType() == JavaTokenTypes.IDENT) {
            if(_insideLoop != null) {
                FlowGraphNode f = _insideLoop.getLoopHead();
                if(f.getType() == FlowGraphNode.FOR_BEGIN) {
                    ForBeginNode fbeg = (ForBeginNode) f;
                    String var = fbeg.getGetWidthLoop();
                    if(var != null && v.getText().equals(var)) {
                        return ALLCHANNELS;
                    }
                    else {
                        return UNDEFCHANNEL;
                    }
                }
                else {
                    return UNDEFCHANNEL;
                }
            }
            else {
               return UNDEFCHANNEL;
            }
        }
        else
        if(v.getType() != JavaTokenTypes.NUM_INT) {
            return UNDEFCHANNEL;
        }
        return (new Integer(v.getText())).intValue();
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private LinkedList _inputEdges;
    private LinkedList _outputEdges;

    private MethodCallNode _fgNode;
    // Se for um no loop head, armazena o respectivo no do FG
    private FlowGraphNode _loopHead;

    // Quando esse no for um loophead, entao esses sao os mGnodes contidos no 
    // corpo do loop
    private HashMap _loopBodyNodes;
    private LinkedList _firstLoopNodes;
    private LinkedList _continueLoopNodes;

    // Se for um dos nos dentro de um loop, essa e a cabeca.
    private MethodGraphNode _insideLoop;
    
    // informacoes sobre o metodo
    private String _port;

    private int _type;
    private int _channel;
    private int _ntokens;

    private HashMap _portsSampleRates;
    private HashMap _SRportsSampleRates;

    private boolean _timed = false;
    
    private boolean _containsIOMethods;
}
